{{>partial_header}}
package {{packageName}}

import (
	"fmt"
	"net/http"
	"strings"
)

// contextKeys are used to identify the type of value in the context.
// Since these are string, it is possible to get a short description of the
// context key for logging and debugging using key.String().

type contextKey string

func (c contextKey) String() string {
	return "auth " + string(c)
}

var (
	// ContextOAuth2 takes an oauth2.TokenSource as authentication for the request.
	ContextOAuth2 = contextKey("token")

	// ContextBasicAuth takes BasicAuth as authentication for the request.
	ContextBasicAuth = contextKey("basic")

	// ContextAccessToken takes a string oauth2 access token as authentication for the request.
	ContextAccessToken = contextKey("accesstoken")

	// ContextAPIKey takes an APIKey as authentication for the request
	ContextAPIKey = contextKey("apikey")

	{{#withAWSV4Signature}}
	// ContextAWSv4 takes an Access Key and a Secret Key for signing AWS Signature v4.
	ContextAWSv4 = contextKey("awsv4")
	{{/withAWSV4Signature}}
)

// BasicAuth provides basic http authentication to a request passed via context using ContextBasicAuth
type BasicAuth struct {
	UserName string `json:"userName,omitempty"`
	Password string `json:"password,omitempty"`
}

// APIKey provides API key based authentication to a request passed via context using ContextAPIKey
type APIKey struct {
	Key    string
	Prefix string
}

{{#withAWSV4Signature}}
// AWSv4 provides AWS Signature to a request passed via context using ContextAWSv4
// https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html
type AWSv4 struct {
		AccessKey string
		SecretKey string
}
{{/withAWSV4Signature}}

// ServerVariable stores the information about a server variable
type ServerVariable struct {
	Description  string
	DefaultValue string
	EnumValues   []string
}

// ServerConfiguration stores the information about a server
type ServerConfiguration struct {
	Url string
	Description string
	Variables map[string]ServerVariable
}

// Configuration stores the configuration of the API client
type Configuration struct {
	BasePath      string            `json:"basePath,omitempty"`
	Host          string            `json:"host,omitempty"`
	Scheme        string            `json:"scheme,omitempty"`
	DefaultHeader map[string]string `json:"defaultHeader,omitempty"`
	UserAgent     string            `json:"userAgent,omitempty"`
	Debug         bool              `json:"debug,omitempty"`
	Servers       []ServerConfiguration
	HTTPClient    *http.Client
}

// NewConfiguration returns a new Configuration object
func NewConfiguration() *Configuration {
	cfg := &Configuration{
		BasePath:      "{{{basePath}}}",
		DefaultHeader: make(map[string]string),
		UserAgent:     "{{#httpUserAgent}}{{{.}}}{{/httpUserAgent}}{{^httpUserAgent}}OpenAPI-Generator/{{{packageVersion}}}/go{{/httpUserAgent}}",
		Debug:         false,
		{{#servers}}
		{{#-first}}
		Servers:       []ServerConfiguration{
		{{/-first}}
			{
				Url: "{{{url}}}",
				Description: "{{{description}}}{{^description}}No description provided{{/description}}",
				{{#variables}}
				{{#-first}}
				Variables: map[string]ServerVariable{
				{{/-first}}
					"{{{name}}}": ServerVariable{
						Description: "{{{description}}}{{^description}}No description provided{{/description}}",
						DefaultValue: "{{{defaultValue}}}",
						{{#enumValues}}
						{{#-first}}
						EnumValues: []string{
						{{/-first}}
							"{{{.}}}",
						{{#-last}}
						},
						{{/-last}}
						{{/enumValues}}
					},
				{{#-last}}
				},
				{{/-last}}
				{{/variables}}
			},
		{{#-last}}
		},
		{{/-last}}
		{{/servers}}
	}
	return cfg
}

// AddDefaultHeader adds a new HTTP header to the default header in the request
func (c *Configuration) AddDefaultHeader(key string, value string) {
	c.DefaultHeader[key] = value
}

// ServerUrl returns URL based on server settings
func (c *Configuration) ServerUrl(index int, variables map[string]string) (string, error) {
	if index < 0 || len(c.Servers) <= index {
		return "", fmt.Errorf("Index %v out of range %v", index, len(c.Servers) - 1)
	}
	server := c.Servers[index]
	url := server.Url

	// go through variables and replace placeholders
	for name, variable := range server.Variables {
		if value, ok := variables[name]; ok {
			found := bool(len(variable.EnumValues) == 0)
			for _, enumValue := range variable.EnumValues {
				if value == enumValue {
					found = true
				}
			}
			if !found {
				return "", fmt.Errorf("The variable %s in the server URL has invalid value %v. Must be %v", name, value, variable.EnumValues)
			}
			url = strings.Replace(url, "{"+name+"}", value, -1)
		} else {
			url = strings.Replace(url, "{"+name+"}", variable.DefaultValue, -1)
		}
	}
	return url, nil
}
